package com.googlecode.pdfbricks.merger;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.googlecode.pdfbricks.engine.PDFOperator;
import com.googlecode.pdfbricks.engine.base.Executable;
import com.googlecode.pdfbricks.engine.base.Validatable;
import com.googlecode.pdfbricks.engine.exception.CommandExecuteException;
import com.googlecode.pdfbricks.engine.exception.CommandValidateException;
import com.googlecode.pdfbricks.engine.exception.PDFOperationException;
import com.googlecode.pdfbricks.engine.to.FilePageRange;
import com.googlecode.pdfbricks.engine.to.PageRange;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfReader;

/**
 * @author Johny Huang
 * @date 2010-1-16
 */
public class MergeCommand implements Executable,Validatable {

	private static final Log log = LogFactory.getLog(MergeCommand.class);
	
	private List fromFilePageRangeList;

	private File toFile;

	public MergeCommand() {
	}
	
	public MergeCommand addFromFilePageRange(FilePageRange filePageRange){
		if(fromFilePageRangeList==null){
			fromFilePageRangeList=new ArrayList();
		}
		fromFilePageRangeList.add(filePageRange);
		return this;
	}

	public void validate(){
		if(toFile==null){
			throw new CommandValidateException("No file path is indicated which pdfbricks should merge to!");
		}
		if(fromFilePageRangeList==null||fromFilePageRangeList.isEmpty()){
			throw new CommandValidateException("Do not indicated the file which pdfbricks should merge from");
		}
		if(toFile.isFile()&&toFile.exists()){
			throw new CommandValidateException("File exists in "+toFile.getAbsolutePath());
		}
		if(!fromFilePageRangeList.isEmpty()){
			int i=0;
			int size=fromFilePageRangeList.size();
			File file=null;
			for(;i<size;i++){
				FilePageRange fpr=(FilePageRange) fromFilePageRangeList.get(i);
				file=new File(fpr.getFilePath());
				if(!file.exists()||!file.isFile()){
					throw new CommandValidateException("PDF file doesn't exist in "+fpr.getFilePath());
				}
			}
		}
	}

	public void run() {
		execute();
	}

	public void execute(){
		validate();
		execute0();
	}
	
	private void execute0(){
		Document document = new Document();
		PdfReader reader = null;
		FilePageRange range=null;
		PageRange pr=null;
		List prList=null;
		int i=0,j;
		int size=fromFilePageRangeList.size();
		int pageRangeSize;
		try {
			PdfCopy copy = new PdfCopy(document, new BufferedOutputStream(
					new FileOutputStream(toFile)));
			document.open();

			// Merge all pages of srcFiles to desFile
			PDFOperator pdfOpt = new PDFOperator();
			
			for (;i<size;i++) {
				range=(FilePageRange) fromFilePageRangeList.get(i);
				reader = new PdfReader(range.getFilePath());
				prList = range.getPageRangeList();

				if (range.isAllPages()) {
					// Reader reads all the page and copy to PdfCopy.
					pdfOpt.extractAll(copy, reader);
				} else {
					pageRangeSize=prList.size();
					for (j=0;j<pageRangeSize;j++) {
						pr=(PageRange) prList.get(j);
						pdfOpt.extract(copy, reader, pr.getStartIndex(), pr
								.getEndIndex());
					}
				}
				reader.close();
			}
		} catch (IOException ex) {
			throw new CommandExecuteException(ex.getMessage(),ex);
		} catch (DocumentException ex) {
			throw new CommandExecuteException(ex.getMessage(),ex);
		} catch(PDFOperationException ex){
			throw new CommandExecuteException(ex.getMessage(),ex);
		} finally {
			if (reader != null) {
				reader.close();
			}
			document.close();
		}
	}
	
	public List getFromFilePageRangeList() {
		return fromFilePageRangeList;
	}

	public void setFromFilePageRangeList(List fromFilePageRangeList) {
		this.fromFilePageRangeList = fromFilePageRangeList;
	}

	public File getToFile() {
		return toFile;
	}

	public void setToFile(File toFile) {
		this.toFile = toFile;
	}
}
